#include "stdafx.h"
#include "Board.h"
#include "ZobristHasher.h"
#include "DefaultBoard.h"

using namespace std;

inline bool str_contains(const string &str, const char *txt)
{
	return str.find(txt) != string::npos;
}

Board::Board()
: CurrentState(NONE)
{
	memset(PieceCounts, 0, sizeof(PieceCounts));
}

Board::Board(const std::string &fen)
	: Board()
{
	byte index = 0;
	byte spc = 0;
	byte spacers = 0;

	WhoseMove = PieceColor::White;

	if (str_contains(fen, "a3"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 40;
	}
	else if (str_contains(fen, "b3"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 41;
	}
	else if (str_contains(fen, "c3"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 42;
	}
	else if (str_contains(fen, "d3"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 43;
	}
	else if (str_contains(fen, "e3"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 44;
	}
	else if (str_contains(fen, "f3"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 45;
	}
	else if (str_contains(fen, "g3"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 46;
	}
	else if (str_contains(fen, "h3"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 47;
	}


	if (str_contains(fen, "a6"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 16;
	}
	else if (str_contains(fen, "b6"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 17;
	}
	else if (str_contains(fen, "c6"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 18;
	}
	else if (str_contains(fen, "d6"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 19;
	}
	else if (str_contains(fen, "e6"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 20;
	}
	else if (str_contains(fen, "f6"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 21;
	}
	else if (str_contains(fen, "g6"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 22;
	}
	else if (str_contains(fen, "h6"))
	{
		EnPassantColor = PieceColor::White;
		EnPassantPosition = 23;
	}

	for (char c : fen)
	{
		if (index < 64 && spc == 0)
		{
			if (c == '1' && index < 63)
			{
				index++;
			}
			else if (c == '2' && index < 62)
			{
				index += 2;
			}
			else if (c == '3' && index < 61)
			{
				index += 3;
			}
			else if (c == '4' && index < 60)
			{
				index += 4;
			}
			else if (c == '5' && index < 59)
			{
				index += 5;
			}
			else if (c == '6' && index < 58)
			{
				index += 6;
			}
			else if (c == '7' && index < 57)
			{
				index += 7;
			}
			else if (c == '8' && index < 56)
			{
				index += 8;
			}
			else if (c == 'P')
			{
				Squares[index] = Piece(PieceType::Pawn, PieceColor::White);
				index++;
			}
			else if (c == 'N')
			{
				Squares[index] = Piece(PieceType::Knight, PieceColor::White);
				index++;
			}
			else if (c == 'B')
			{
				Squares[index] = Piece(PieceType::Bishop, PieceColor::White);
				index++;
			}
			else if (c == 'R')
			{
				Squares[index] = Piece(PieceType::Rook, PieceColor::White);
				index++;
			}
			else if (c == 'Q')
			{
				Squares[index] = Piece(PieceType::Queen, PieceColor::White);
				index++;
			}
			else if (c == 'K')
			{
				Squares[index] = Piece(PieceType::King, PieceColor::White);
				index++;
			}
			else if (c == 'p')
			{
				Squares[index] = Piece(PieceType::Pawn, PieceColor::Black);
				index++;
			}
			else if (c == 'n')
			{
				Squares[index] = Piece(PieceType::Knight, PieceColor::Black);
				index++;
			}
			else if (c == 'b')
			{
				Squares[index] = Piece(PieceType::Bishop, PieceColor::Black);
				index++;
			}
			else if (c == 'r')
			{
				Squares[index] = Piece(PieceType::Rook, PieceColor::Black);
				index++;
			}
			else if (c == 'q')
			{
				Squares[index] = Piece(PieceType::Queen, PieceColor::Black);
				index++;
			}
			else if (c == 'k')
			{
				Squares[index] = Piece(PieceType::King, PieceColor::Black);
				index++;
			}
			else if (c == '/')
			{
				continue;
			}
			else if (c == ' ')
			{
				spc++;
			}
		}
		else
		{
			if (c == 'w')
			{
				WhoseMove = PieceColor::White;
			}
			else if (c == 'b')
			{
				WhoseMove = PieceColor::Black;
			}
			else if (c == 'K')
			{
				if (Squares[60])
				{
					if (Squares[60].Type == PieceType::King)
					{
						//CurrentState &= ~W_KING_MOVED;
						//Squares[60].Moved = false;
					}
				}

				if (Squares[63])
				{
					if (Squares[63].Type == PieceType::Rook)
					{
						//CurrentState &= ~W_
						//Squares[63].Moved = false;
					}
				}

				CurrentState &= ~WHITE_CASTLED;
				//WhiteCastled = false;
			}
			else if (c == 'Q')
			{
				if (Squares[60])
				{
					if (Squares[60].Type == PieceType::King)
					{
						//Squares[60].Moved = false;
					}
				}

				if (Squares[56])
				{
					if (Squares[56].Type == PieceType::Rook)
					{
						//Squares[56].Moved = false;
					}
				}

				//WhiteCastled = false;
			}
			else if (c == 'k')
			{
				if (Squares[4])
				{
					if (Squares[4].Type == PieceType::King)
					{
						//Squares[4].Moved = false;
					}
				}

				if (Squares[7])
				{
					if (Squares[7].Type == PieceType::Rook)
					{
						//Squares[7].Moved = false;
					}
				}

				//BlackCastled = false;
			}
			else if (c == 'q')
			{
				if (Squares[4])
				{
					if (Squares[4].Type == PieceType::King)
					{
						//Squares[4].Moved = false;
					}
				}

				if (Squares[0])
				{
					if (Squares[0].Type == PieceType::Rook)
					{
						//Squares[0].Moved = false;
					}
				}

				//BlackCastled = false;
			}
			else if (c == ' ')
			{
				spacers++;
			}
			else if (c == '1' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 1);
			}
			else if (c == '2' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 2);
			}
			else if (c == '3' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 3);
			}
			else if (c == '4' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 4);
			}
			else if (c == '5' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 5);
			}
			else if (c == '6' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 6);
			}
			else if (c == '7' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 7);
			}
			else if (c == '8' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 8);
			}
			else if (c == '9' && spacers == 4)
			{
				FiftyMove = (byte) ((FiftyMove * 10) + 9);
			}
			else if (c == '0' && spacers == 4)
			{
				MoveCount = (byte) ((MoveCount * 10) + 0);
			}
			else if (c == '1' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 1);
			}
			else if (c == '2' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 2);
			}
			else if (c == '3' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 3);
			}
			else if (c == '4' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 4);
			}
			else if (c == '5' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 5);
			}
			else if (c == '6' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 6);
			}
			else if (c == '7' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 7);
			}
			else if (c == '8' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 8);
			}
			else if (c == '9' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 9);
			}
			else if (c == '0' && spacers == 5)
			{
				MoveCount = (byte) ((MoveCount * 10) + 0);
			}

		}

		for (size_t i = 0; i < NUM_SQUARES; ++i)
		{
			Piece p = Squares[i];

			if (p)
				UpdatePieceCount(p.Color, p.Type, 1);
		}

		ZobristHasher.Initialize(*this);
	}
}

Board::Board(bool def)
: CurrentState(NONE)
{
	memcpy(Squares, DEFAULT_BOARD, sizeof(DEFAULT_BOARD));
	memcpy(PieceCounts, DEFAULT_COUNTS, sizeof(DEFAULT_COUNTS));
}

bool Board::PromotePawns(Piece piece, byte dstPosition)
{
	if (piece.Type == PieceType::Pawn)
	{
		if (dstPosition < 8 || dstPosition > 55)
		{
			Squares[dstPosition].Type = PieceType::Queen;
			return true;
		}
	}
	else
		return false;
}

void Board::RecordEnPassant(PieceColor color, PieceType type, byte srcPos, byte dstPos)
{
	if (type == PieceType::Pawn)
	{
		FiftyMove = 0;

		byte difference = srcPos - dstPos;

		// Drop the sign and see if the move was up or down 2 rows (16 spaces)
		if ((difference & ~0x80) == 16)
		{
			EnPassantPosition = dstPos + difference / 2;
			EnPassantColor = color;
		}
	}
}

void Board::DoMove(const Move &m)
{
	FiftyMove++;

	if (m.Piece1 == PieceColor::Black)
		MoveCount++;

	Squares[m.SrcPos1] = false;
	Squares[m.DestPos1] = m.Piece1;

	switch (m.Type)
	{
	case MoveType::Capture:
		// Reduce the count of pieces
		UpdatePieceCount(!m.Piece1.Color, m.Captured, -1);
		break;

	case MoveType::Castle:
		Squares[m.SrcPos2] = false;
		Squares[m.DestPos2] = Piece(PieceType::Rook, m.Piece1.Color);
		break;

	case MoveType::EnPassant:
		// Clear the square
		Squares[m.DestPos2] = false;

		// Remove a pawn from the other player
		UpdatePieceCount(!m.Piece1.Color, PieceType::Pawn, -1);
		break;
	}

	if (m.Flags & PROMOTED)
	{
		Squares[m.DestPos1].Type = PieceType::Queen;

		UpdatePieceCount(m.Piece1.Color, PieceType::Pawn, -1);
		UpdatePieceCount(m.Piece1.Color, PieceType::Queen, 1);
	}

	ZobristHasher.DoMove(*this, m);

	// Update the flags from the move
	CurrentState |= m.Flags;

	WhoseMove = !WhoseMove;
}

void Board::UndoMove(const Move &m)
{
	FiftyMove--;

	if (m.Piece1 == PieceColor::Black)
		MoveCount--;

	Squares[m.SrcPos1] = m.Piece1;

	switch (m.Type)
	{
	case MoveType::Normal:
		Squares[m.DestPos1] = false;
		break;
	case MoveType::Capture:
		Squares[m.DestPos1] = Piece(m.Captured, !m.Piece1.Color);

		UpdatePieceCount(!m.Piece1.Color, m.Captured, 1);
		break;
	case MoveType::Castle:
		Squares[m.DestPos1] = false;
		Squares[m.SrcPos2] = Piece(PieceType::Rook, m.Piece1.Color);
		Squares[m.DestPos2] = false;
		break;
	case MoveType::EnPassant:
		Squares[m.DestPos1] = false;
		Squares[m.DestPos2] = Piece(PieceType::Pawn, !m.Piece1.Color);

		UpdatePieceCount(!m.Piece1.Color, PieceType::Pawn, 1);
		break;
	}

	ZobristHasher.UndoMove(*this, m);

	CurrentState &= ~m.Flags;

	WhoseMove = !WhoseMove;
}

void Board::UpdatePieceCount(PieceColor color, PieceType type, int dx)
{
	byte idx = (byte) type;
	
	switch (color)
	{
	case PieceColor::Black:
		idx += 6;
	}
	PieceCounts[idx] += dx;
}
